package com.peas.platform.common.core.component;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.peas.platform.common.core.annotation.Invoke;
import com.peas.platform.common.core.aop.BaseAopExecution;
import com.peas.platform.common.core.configuration.AlphaAopBean;
import com.peas.platform.common.core.configuration.RequestContext;
import com.peas.platform.common.core.configuration.RequestContextUtil;
import com.peas.platform.common.core.dto.ResponseDto;
import com.peas.platform.common.core.enums.Execution;
import com.peas.platform.common.core.feign.PeasModuleSysCoreFeign;
import com.peas.platform.common.core.utils.LogUtil;
import com.peas.platform.common.core.utils.RequestUtil;
import com.peas.platform.common.core.utils.SystemTool;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.lang.reflect.Method;

@Component
@Slf4j
public class AopExecutionInvoke implements BaseAopExecution {

    @Value("${spring.profiles.active:local}")
    private String profiles;

    @Resource
    private ApplicationContext ac;
    @Resource
    private SystemTool systemTool;

    @Override
    public Execution exec() {
        return Execution.THREAD;
    }

    @Override
    public void execution(final AlphaAopBean aab) {
        Class<?> clazz = aab.getClazz();
        Method method = aab.getMethod();
        boolean needInvoke = true;
        Invoke invokeOnClass = clazz.getAnnotation(Invoke.class);
        Controller controllerOnClass = clazz.getAnnotation(Controller.class);
        RestController restControllerOnClass = clazz.getAnnotation(RestController.class);
        RequestMapping requestMappingOnClass = clazz.getAnnotation(RequestMapping.class);
        Invoke invokeOnMethod = method.getAnnotation(Invoke.class);
        GetMapping getMappingOnMethod = method.getAnnotation(GetMapping.class);
        PostMapping postMappingOnMethod = method.getAnnotation(PostMapping.class);
        PutMapping putMappingOnMethod = method.getAnnotation(PutMapping.class);
        DeleteMapping deleteMappingOnMethod = method.getAnnotation(DeleteMapping.class);
        RequestMapping requestMappingOnMethod = method.getAnnotation(RequestMapping.class);
        if (requestMappingOnClass == null) {
            log.debug("[needInvoke]:requestMappingOnClass == null");
            needInvoke = false;
        }
        if (controllerOnClass == null && restControllerOnClass == null) {
            log.debug("[needInvoke]:controllerOnClass == null && restControllerOnClass == null");
            needInvoke = false;
        }
        if (invokeOnClass != null && !invokeOnClass.save()) {
            log.debug("[needInvoke]:invokeOnClass != null && !invokeOnClass.save()");
            needInvoke = false;
        }
        if (invokeOnMethod == null && getMappingOnMethod == null && postMappingOnMethod == null && putMappingOnMethod == null && deleteMappingOnMethod == null && requestMappingOnMethod == null) {
            log.debug("[needInvoke]:invokeOnMethod == null && getMappingOnMethod == null && postMappingOnMethod == null && putMappingOnMethod == null && deleteMappingOnMethod == null && requestMappingOnMethod == null");
            needInvoke = false;
        }
        if (invokeOnMethod != null && !invokeOnMethod.save()) {
            log.debug("[needInvoke]:invokeOnMethod != null && !invokeOnMethod.save()");
            needInvoke = false;
        }
        if (needInvoke) {
            if (!StrUtil.equals("local", profiles)) {
                switch (this.exec()) {
                    case NONE:
                        break;
                    case LOG:
                        String content = JSONUtil.toJsonStr(aab);
                        LogUtil.log(aab.getClazz(), aab.getMethod().getName(), content, aab.getDuration(), "AOP Invoke");
                        break;
                    case SYNC:
                        invoke(clazz, method, aab.getArgs().length == 1 ? JSONUtil.toJsonStr(aab.getArgs()[0]) : JSONUtil.toJsonStr(aab.getArgs()), StrUtil.sub(JSONUtil.toJsonStr(aab.getResult()), 0, 2000), aab.getDuration());
                        break;
                    case THREAD: {
                        final RequestContext rc = RequestContextUtil.getRequestContext();
                        new Thread(() -> {
                            RequestUtil.initRequestId(rc);
                            invoke(clazz, method, aab.getArgs().length == 1 ? JSONUtil.toJsonStr(aab.getArgs()[0]) : JSONUtil.toJsonStr(aab.getArgs()), StrUtil.sub(JSONUtil.toJsonStr(aab.getResult()), 0, 2000), aab.getDuration());
                        }).start();
                        break;
                    }
                }
            }
        }
    }

    private void invoke(Class<?> clazz, Method method, String input, String output, long duration) {
        if (systemTool.special()) {
            LogUtil.log(this.getClass(), "invoke", "clazz:" + clazz.getName() + ",method:" + method.getName() + ",input:" + input + ",output:" + output, duration);
            return;
        }
        Class<?> c = method.getDeclaringClass();
        String uri = clazz.getAnnotation(RequestMapping.class).value()[0];
        String m = StrUtil.EMPTY;
        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
        if (requestMapping != null) {
            uri = uri + requestMapping.value()[0];
            m = requestMapping.method()[0].name().toUpperCase();
        }
        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        if (getMapping != null) {
            uri = uri + getMapping.value()[0];
            m = "GET";
        }
        PostMapping postMapping = method.getAnnotation(PostMapping.class);
        if (postMapping != null) {
            uri = uri + postMapping.value()[0];
            m = "POST";
        }
        PutMapping putMapping = method.getAnnotation(PutMapping.class);
        if (putMapping != null) {
            uri = uri + putMapping.value()[0];
            m = "PUT";
        }
        DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class);
        if (deleteMapping != null) {
            uri = uri + deleteMapping.value()[0];
            m = "DELETE";
        }
        JSONObject data = systemTool.buildJSONObject();
        ApiOperation ao = method.getAnnotation(ApiOperation.class);
        if (ao != null) {
            data.set("interfaceDesc", ao.value());
        }
        data.set("interfaceUrl", uri);
        data.set("interfaceParam", StrUtil.sub(input, 0, 2000));
        data.set("interfaceMethod", m);
        data.set("clazz", c.getName());
        data.set("method", method.getName());
        data.set("returnData", StrUtil.sub(output, 0, 2000));
        data.set("invokeRequestId", RequestContextUtil.getCurrentRequestId());
        data.set("invokeTime", DateUtil.date());
        data.set("invokeDuration", duration);
        log.debug("addInvoke:" + data);
        log.debug("call peasModuleSysMdmFeign.addInvoke:" + JSONUtil.toJsonStr(data));
        PeasModuleSysCoreFeign peasModuleSysCoreFeign = ac.getBean(PeasModuleSysCoreFeign.class);
        ResponseDto<Integer> result = peasModuleSysCoreFeign.addInvoke(data);
        log.debug("call peasModuleSysMdmFeign.addInvoke, result:" + result);
    }

}
